package com.hdu.kdk_es_orm.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class ReflectionUtils {

    public enum Type {
        CLASS,
        FIELD,
        METHOD
    }

    public static <A extends Annotation> A getAnnotationOnMethods(Class<?> clazz, Class<A> targetAnnotation) {
        validateInput(clazz, targetAnnotation);
        return getAnnotation(clazz, targetAnnotation, Type.METHOD);
    }

    public static <A extends Annotation> A getAnnotationOnClass(Class<?> clazz, Class<A> targetAnnotation) {
        validateInput(clazz, targetAnnotation);
        return getAnnotation(clazz, targetAnnotation, Type.CLASS);
    }

    public static <A extends Annotation> A getAnnotationOnFields(Object obj, Class<A> targetAnnotation) {
        validateInput(obj.getClass(), targetAnnotation);
        return getAnnotation(obj.getClass(), targetAnnotation, Type.FIELD);
    }

    public static <A extends Annotation> A getAnnotationOnFields(Class<?> targetClass, Class<A> targetAnnotation) {
        validateInput(targetClass, targetAnnotation);
        return getAnnotation(targetClass, targetAnnotation, Type.FIELD);
    }

    /**
     * 查找属性值，该属性上面加了 targetAnnotation 注解
     */
    public static Object getFieldValueByTargetAnnotation(Class<? extends Annotation> targetAnnotation, Object obj) {
        validateInput(targetAnnotation, obj);
        List<Field> validFields = filterFieldsByTargetAnnotation(obj.getClass(), targetAnnotation);
        if (validFields.size() != 1) {
            throw new IllegalArgumentException("找到多个 " + targetAnnotation.getSimpleName() + " 注解。");
        }
        try {
            Field declaredField = validFields.get(0);
            declaredField.setAccessible(true);
            return declaredField.get(obj);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("访问字段 " + obj.getClass().getSimpleName() + " 失败。", e);
        }
    }

    /**
     * 设置属性值，该属性上面加了 targetAnnotation 注解
     */
    public static void setFiledValueByTargetAnnotation(Class<? extends Annotation> targetAnnotation,
                                                       Object obj,
                                                       Object value) {
        validateInput(targetAnnotation, obj);
        List<Field> validFields = filterFieldsByTargetAnnotation(obj.getClass(), targetAnnotation);
        if (validFields.size() != 1) {
            throw new IllegalArgumentException("找到多个 " + targetAnnotation.getSimpleName() + " 注解。");
        }
        try {
            Field declaredField = validFields.get(0);
            declaredField.setAccessible(true);
            declaredField.set(obj, value);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("访问字段 " + obj.getClass().getSimpleName() + " 失败。", e);
        }
    }

    /**
     * 获得注解
     */
    private static <A extends Annotation> A getAnnotation(Class<?> clazz, Class<A> targetAnnotation, Type type) {
        switch (type) {
            case CLASS:
                return clazz.getAnnotation(targetAnnotation);
            case FIELD:
                List<Field> validFields = filterFieldsByTargetAnnotation(clazz, targetAnnotation);
                if (validFields.size() != 1) {
                    throw new IllegalArgumentException("找到多个 " + targetAnnotation.getSimpleName() + " 注解。");
                }
                return validFields.get(0).getAnnotation(targetAnnotation);
            case METHOD:
                List<Method> validMethods = filterMethodByTargetAnnotation(clazz, targetAnnotation);
                if (validMethods.size() != 1) {
                    throw new IllegalArgumentException("找到多个 " + targetAnnotation.getSimpleName() + " 注解。");
                }
                return validMethods.get(0).getAnnotation(targetAnnotation);
            default:
                throw new IllegalArgumentException("未知的类型: " + type);
        }
    }

    /**
     * 过滤出标记了 targetAnnotation 注解的字段
     */
    private static List<Field> filterFieldsByTargetAnnotation(Class<?> aClazz, Class<? extends Annotation> targetAnnotation) {
        Field[] declaredFields = aClazz.getDeclaredFields();
        return Arrays.stream(declaredFields)
                .filter(declaredField -> Objects.nonNull(declaredField.getAnnotation(targetAnnotation)))
                .collect(Collectors.toList());
    }

    /**
     * 过滤出标记了 targetAnnotation 注解的方法
     */
    private static List<Method> filterMethodByTargetAnnotation(Class<?> aClazz, Class<? extends Annotation> targetAnnotation) {
        Method[] declaredMethods = aClazz.getDeclaredMethods();
        return Arrays.stream(declaredMethods)
                .filter(declaredMethod -> Objects.nonNull(declaredMethod.getAnnotation(targetAnnotation)))
                .collect(Collectors.toList());
    }

    private static void validateInput(Class<?> clazz, Class<? extends Annotation> targetAnnotation) {
        if (clazz == null || targetAnnotation == null) {
            throw new IllegalArgumentException("类和注解参数不能为空。");
        }
    }

    private static void validateInput(Class<? extends Annotation> targetAnnotation, Object obj) {
        if (targetAnnotation == null || obj == null) {
            throw new IllegalArgumentException("注解和对象参数不能为空。");
        }
    }
}
